package org.muninn.niagara.services;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;

import javax.baja.collection.BITable;
import javax.baja.collection.TableCursor;
import javax.baja.file.BExporter;
import javax.baja.file.ExportOp;
import javax.baja.fox.BFoxProxySession;
import javax.baja.naming.BHost;
import javax.baja.naming.BOrd;
import javax.baja.naming.OrdTarget;
import javax.baja.security.BPassword;
import javax.baja.sys.Action;
import javax.baja.sys.BBoolean;
import javax.baja.sys.BComponent;
import javax.baja.sys.BIcon;
import javax.baja.sys.BInteger;
import javax.baja.sys.BRelTime;
import javax.baja.sys.BStation;
import javax.baja.sys.BString;
import javax.baja.sys.Flags;
import javax.baja.sys.Property;
import javax.baja.sys.Sys;
import javax.baja.sys.Type;

import com.tridium.platform.BSystemPlatformService;

public class BAuditService extends BMuninnService
{
  /** Allow service to be executed (automatically set to false every time service is executed to prevent overworking the processor) **/
  public static final Property enabled = newProperty(0, false);
  public void setEnabled(boolean v) { setBoolean(enabled, v); }
  public boolean getEnabled() { return getBoolean(enabled); }
  
  /** Audit target JACE IP */
  public static final Property targetIP = newProperty(0, "192.168.0.24");
  public void setTargetIP(String v) { setString(targetIP, v); }
  public String getTargetIP() { return getString(targetIP); }

  /** Audit target JACE port */
  public static final Property port = newProperty(0, "1911");
  public void setPort(String v) { setString(port, v); }
  public String getPort() { return getString(port); }

  /** Audit target JACE user name */
  public static final Property username = newProperty(0, "admin");
  public void setUsername(String v) { setString(username, v); }
  public String getUsername() { return getString(username); }

  /** Audit target JACE password */
  public static final Property password = newProperty(0, BPassword.make(""));
  public void setPassword(BPassword v) { set(password, v); }
  public BPassword getPassword() { return (BPassword) get(password); }

  /** Audit output folder */
  public static final Property outPath = newProperty(0, "C:\\Niagara\\Niagara-3.7.46\\stations\\AXServer\\audit\\out\\");
  public void setOutPath(String v) { setString(outPath, v); }
  public String getOutPath() { return getString(outPath); }
	  
  /** Search Slot Path */
  public static final Property searchSlotPath = newProperty(0, "slot:/");
  public void setSearchSlotPath(String v) { setString(searchSlotPath, v); }
  public String getSearchSlotPath() { return getString(searchSlotPath); }
	  
  private static BAuditService INSTANCE = null;
  public static final Action execute = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void execute() { invoke(execute, null, null); }
  
  /*************************/
  /*   Begin - Utilities   */
  /*************************/
  private BComponent getComponent(BStation station, String slotPath)
  {
	  BComponent component = BOrd.make(slotPath).resolve(station).getComponent();
	  component.lease();
	  return component;
  }

//  private int getChildrenCount(BStation station, String slotPath)
//  {
//	  return getComponent(station, slotPath).getChildComponents().length;
//  }

  private String getTypeCount(BStation station, String typeName)
  {
	  BITable table = (BITable)BOrd.make("station:|slot:/|bql:select name from " + typeName).resolve(station).get();
	  return Integer.toString(table.size());
  }

  private String getBQLInteger(BStation station, String bql)
  {
	  BInteger value = (BInteger)BOrd.make(bql).resolve(station).get();
	  return value.toString();
  }

  private String getBQLString(BStation station, String bql)
  {
	  BString value = (BString)BOrd.make(bql).resolve(station).get();
	  return value.toString();
  }
  /***********************/
  /*   End - Utilities   */
  /***********************/

  public void doExecute() throws Exception
  {
	String ipAddress = getTargetIP();
	String port = getPort();
	String username = getUsername();
	String password = getPassword().getString();
	String outPath = getOutPath();
	String searchSlotPath = getSearchSlotPath();

    System.out.println("\nMuninn Audit Service . . .");

    if (!getEnabled())
    {
        System.out.println("   Not enabled.");
        System.out.println("Muninn Audit Service done.\n");
    	return;
    }
    
    try
    {
	    BFoxProxySession foxProxySession = openSession(ipAddress, port, username, password);   
	    BStation station = (BStation)BOrd.make("station:|slot:/").get(foxProxySession);     
	    
	    createHeaderFile(ipAddress, station, outPath);
	    createDetailFile(station, outPath, searchSlotPath);
	    createPxViewFile(station, outPath);
	    createNameValueFile(station, outPath);
	    
	    closeSession(foxProxySession);   
    }
    catch (Exception e)
    {
    	System.out.println("   *** Fatal Exception in doExecute().");	
    	System.out.println(e.toString());	
    }

    System.out.println("Muninn Audit Service done.\n");
  }

  private BFoxProxySession openSession(String ipAddress, String port, String username, String password) throws Exception
  {
	System.out.println("   Open session to " + ipAddress + ":" + port + " using " + username + "/" + password + " . . ."); 
    BFoxProxySession session = BFoxProxySession.make((BHost)BOrd.make("ip:" + ipAddress).get(), Integer.parseInt(port), username, password);
    System.out.println("      Engage session . . .");
    session.engageNoRetry("session"); 
    System.out.println("      Session engaged.");
    return session;
  }

  private void closeSession(BFoxProxySession session) throws Exception
  {
	System.out.println("   Close session . . ."); 
    session.disengage("session");
  } 
  
  private void createHeaderFile(String ipAddress, BStation station, String outPath) throws Exception
  {    
	  try
	  {
		station.lease();
		String filePath = outPath + "MuninnAuditService_" + station.getStationName() + "_Header.csv";
		System.out.println("   Create Header file " + filePath + " . . .");  
	    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
		System.out.println("      Write column headers to file . . .");  
	
	    bufferedWriter.write(
	    	"ipAddress, " + 
	    	"stationName, " + 
	    	"tuningPolicyTouched, " + 
	    	"scheduleTouched, " + 
	    	"defaultAlarmClassCount"); 
	    
	    bufferedWriter.newLine();
		System.out.println("      Write data to file . . .");  
	
		bufferedWriter.write(
	    	ipAddress + ", " + 
	    	station.getStationName() + ", " + 
	    	tuningPolicyTouched(station) + ", " + 
	    	scheduleTouched(station) + ", " + 
	    	alarmSourceInfoAlarmClassDefaultCount(station));
	    
	    bufferedWriter.close();
	  }
	  catch (Exception e)
	  {
	  	System.out.println("   *** Fatal Exception in createHeaderFile().");	
	  	System.out.println(e.toString());	
	  }
  }
  
  private String lonworksTuningPolicyTouched(BStation station) throws Exception
  {
	  station.lease();
	  try
	  {
		  String minWriteTime;
		  String maxWriteTime;
		  String writeOnStart;
		  String writeOnUp;
		  String writeOnEnabled;
		  String staleTime;
		  String pollFrequency;
		  String writeDelay;
		  
		  String fields = "minWriteTime, maxWriteTime, writeOnStart, writeOnUp, writeOnEnabled, staleTime, pollFrequency, writeDelay";
				  
		  BITable table = (BITable)BOrd.make("station:|slot:/|bql:select " + fields + " from lonworks:LonTuningPolicy").resolve(station).get();
		  
		  TableCursor tableCursor = (TableCursor)table.cursor();
	
		  while (tableCursor.next())  
		  {
			  minWriteTime = ((BRelTime)tableCursor.get(table.getColumns().get(0))).toString();
			  maxWriteTime = ((BRelTime)tableCursor.get(table.getColumns().get(1))).toString();
			  writeOnStart = ((BBoolean)tableCursor.get(table.getColumns().get(2))).toString();
			  writeOnUp = ((BBoolean)tableCursor.get(table.getColumns().get(3))).toString();
			  writeOnEnabled = ((BBoolean)tableCursor.get(table.getColumns().get(4))).toString();
			  staleTime = ((BRelTime)tableCursor.get(table.getColumns().get(5))).toString();
			  pollFrequency = ((BString)tableCursor.get(table.getColumns().get(6))).getString();
			  writeDelay = ((BInteger)tableCursor.get(table.getColumns().get(7))).toString();
			  
//			  System.out.println("lonworks");
//			  System.out.println(minWriteTime);
//			  System.out.println(maxWriteTime);
//			  System.out.println(writeOnStart);
//			  System.out.println(writeOnUp);
//			  System.out.println(writeOnEnabled);
//			  System.out.println(staleTime);
//			  System.out.println(pollFrequency);
//			  System.out.println(writeDelay);

			  if (!minWriteTime.equals("0ms") ||
				  !maxWriteTime.equals("1min") ||
				  !writeOnStart.equals("true") ||
				  !writeOnUp.equals("true") ||
				  !writeOnEnabled.equals("true") ||
				  !staleTime.equals("0ms") ||
				  !pollFrequency.equals("Normal") ||
				  !writeDelay.equals("0")) {
//				  	System.out.println("lon=True");
				  	return "True";}
		  }
//		  System.out.println("lon=False");
		  return "False";
	  }
	  catch (Exception e)
	  {
//		  System.out.println("lon error=False");
		  return "False";
	  }
  }

  private String bacnetTuningPolicyTouched(BStation station) throws Exception
  {
	  station.lease();
	  try
	  {
		  String minWriteTime;
		  String maxWriteTime;
		  String writeOnStart;
		  String writeOnUp;
		  String writeOnEnabled;
		  String staleTime;
		  String pollFrequency;
		  String useCov;
		  String useConfirmedCov;
		  String covSubscriptionLifetime;
		  
		  String fields = "minWriteTime, maxWriteTime, writeOnStart, writeOnUp, writeOnEnabled, staleTime, pollFrequency, useCov, useConfirmedCov, covSubscriptionLifetime";
				  
		  BITable table = (BITable)BOrd.make("station:|slot:/|bql:select " + fields + " from bacnet:BacnetTuningPolicy").resolve(station).get();
		  
		  TableCursor tableCursor = (TableCursor)table.cursor();
	
		  while (tableCursor.next())  
		  {
			  minWriteTime = ((BRelTime)tableCursor.get(table.getColumns().get(0))).toString();
			  maxWriteTime = ((BRelTime)tableCursor.get(table.getColumns().get(1))).toString();
			  writeOnStart = ((BBoolean)tableCursor.get(table.getColumns().get(2))).toString();
			  writeOnUp = ((BBoolean)tableCursor.get(table.getColumns().get(3))).toString();
			  writeOnEnabled = ((BBoolean)tableCursor.get(table.getColumns().get(4))).toString();
			  staleTime = ((BRelTime)tableCursor.get(table.getColumns().get(5))).toString();
			  pollFrequency = ((BString)tableCursor.get(table.getColumns().get(6))).getString();
			  useCov = ((BBoolean)tableCursor.get(table.getColumns().get(7))).toString();
			  useConfirmedCov = ((BBoolean)tableCursor.get(table.getColumns().get(8))).toString();
			  covSubscriptionLifetime = ((BInteger)tableCursor.get(table.getColumns().get(9))).toString();
			  
//			  System.out.println("bacnet");
//			  System.out.println(minWriteTime);
//			  System.out.println(maxWriteTime);
//			  System.out.println(writeOnStart);
//			  System.out.println(writeOnUp);
//			  System.out.println(writeOnEnabled);
//			  System.out.println(staleTime);
//			  System.out.println(pollFrequency);
//			  System.out.println(useCov);
//			  System.out.println(useConfirmedCov);
//			  System.out.println(covSubscriptionLifetime);

			  if (!minWriteTime.equals("0ms") ||
				  !maxWriteTime.equals("0ms") ||
				  !writeOnStart.equals("true") ||
				  !writeOnUp.equals("true") ||
				  !writeOnEnabled.equals("true") ||
				  !staleTime.equals("0ms") ||
				  !pollFrequency.equals("Normal") ||
				  !useCov.equals("false") ||
				  !useConfirmedCov.equals("true") ||
				  !covSubscriptionLifetime.equals("15")) {
//				  	System.out.println("bacnet=True");
				  	return "True";}
		  }
//		  System.out.println("bacnet=False");
		  return "False";
	  }
	  catch (Exception e)
	  {
//		  System.out.println("bacnet error=False");
		  return "False";
	  }
  }
  
  private String tuningPolicyTouched(BStation station) throws Exception
  {
	  try
	  {
		  String bacnet = bacnetTuningPolicyTouched(station);
		  String lon = lonworksTuningPolicyTouched(station);
		  
		  if (bacnet.equals("True") && lon.equals("True"))
			  return "True";
		  else
			  return "False";
			  
	  }
	  catch (Exception e)
	  {
		   return "False";
	  }
  }

  private String scheduleTouched(BStation station) throws Exception
  {
	  try
	  {
		  station.lease();
		  List list = new ArrayList();
		  list.add("slot:/Drivers/scheduleLinks/SCH001_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH002_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH003_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH004_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH005_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH006_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH007_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH008_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH009_OCC_C");
		  list.add("slot:/Drivers/scheduleLinks/SCH010_OCC_C");
		  
		  int count = 0;
		  BComponent component = null;
	
		  for (int i = 0; i < list.size(); i++)
		  {
			  component = getComponent(station, list.get(i).toString());
			  count += component.getKnobCount();
		  }
		  return Integer.toString(count);
	  }
	  catch (Exception e)
	  {
		  return "-1";
	  }
  }

  private String alarmSourceInfoAlarmClassDefaultCount(BStation station) throws Exception
  {
	  try
	  {
		  station.lease();
		  int count = 0;
		  
		  String alarmClass;
		  
		  String fields = "alarmClass";
				  
		  BITable table = (BITable)BOrd.make("station:|slot:|bql:select " + fields + " from alarm:AlarmSourceInfo").resolve(station).get();
		  
		  TableCursor tableCursor = (TableCursor)table.cursor();
	
		  while (tableCursor.next())  
		  {
			  alarmClass = tableCursor.get(table.getColumns().get(0)).toString();
			  
			  if (alarmClass.equals("pingAlarmClass")) count++;
		  }
		  return Integer.toString(count);
	  }
	  catch (Exception e)
	  {
		  return "-1";
	  }
  }

  private void createDetailFile(BStation station, String outPath, String searchSlotPath) throws Exception
  {     
	try
	{
		if (searchSlotPath.isEmpty()) searchSlotPath = "station:|slot:/";
		
		station.lease();
		String filePath = outPath + "MuninnAuditService_" + station.getStationName() + "_Detail.csv";
		System.out.println("   Create Detail file " + filePath + " . . .");  
	
		// Get data table.
		System.out.println("      Get bql data table using " + searchSlotPath + " . . .");  
	    OrdTarget ordTarget = BOrd.make(searchSlotPath + "|bql:select name, displayName, facets, slotPath, type, out.value").resolve(station);     
	
	    // Setup conversion.
		System.out.println("      Convert table . . .");  
	    BExporter exporter = (BExporter)Sys.getType("file:ITableToCsv").getInstance(); 
	    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
	    ExportOp exportOp = ExportOp.make(ordTarget, byteArrayOutputStream);  
	    
	    // Perform conversion.
	    exporter.export(exportOp);
	                          
	    // Write converted data table.                        
		System.out.println("      Write data to file . . .");  
	    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
	    bufferedWriter.write(new String(byteArrayOutputStream.toByteArray()));
	    bufferedWriter.close();    
    }
    catch (Exception e)
    {
    	System.out.println("   *** Fatal Exception in createDetailFile().");	
    	System.out.println(e.toString());	
    }
  }
  
  private void createPxViewFile(BStation station, String outPath) throws Exception
  {  
	  try
	  {
		station.lease();
		String filePath = outPath + "MuninnAuditService_" + station.getStationName() + "_PxView.csv";
		System.out.println("   Create PxView file " + filePath + " . . .");  
	
		// Get data table.
		System.out.println("      Get bql data table . . .");  
	    OrdTarget ordTarget = BOrd.make("slot:/|bql:select name, displayName, type, parent.name as 'Parent Name', parent.displayName as 'Parent Display Name', parent.slotPath as 'Parent Slot Path', parent.type as 'Parent Type', pxFile from baja:PxView").resolve(station);     
	
	    // Setup conversion.
		System.out.println("      Convert table . . .");  
	    BExporter exporter = (BExporter)Sys.getType("file:ITableToCsv").getInstance(); 
	    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
	    ExportOp exportOp = ExportOp.make(ordTarget, byteArrayOutputStream);  
	    
	    // Perform conversion.
	    exporter.export(exportOp);
	                          
	    // Write converted data table.                        
		System.out.println("      Write data to file . . .");  
	    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
	    bufferedWriter.write(new String(byteArrayOutputStream.toByteArray()));
	    bufferedWriter.close();    
	  }
	  catch (Exception e)
	  {
	  	System.out.println("   *** Fatal Exception in createPxViewFile().");	
	  	System.out.println(e.toString());	
	  }
  }
  
  private void createNameValueFile(BStation station, String outPath) throws Exception
  {           
	  try
	  {
		String filePath = outPath + "MuninnAuditService_" + station.getStationName() + "_NameValue.csv";
		System.out.println("   Create Name Value file " + filePath + " . . .");  
	    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
		System.out.println("      Write column headers to file . . .");  
	    bufferedWriter.write("Name, Slot_Path, Value"); 
	    bufferedWriter.newLine();
		System.out.println("      Write data to file . . .");
	
		bufferedWriter.write("Component_Count, slot:, " + getTypeCount(station, "baja:Component")); bufferedWriter.newLine();
		bufferedWriter.write("currentCpuUsage, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/currentCpuUsage")); bufferedWriter.newLine();
		bufferedWriter.write("overallCpuUsage, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/overallCpuUsage")); bufferedWriter.newLine();
		bufferedWriter.write("maxFileDescriptors, slot:, 2000"); bufferedWriter.newLine();
		bufferedWriter.write("openFileDescriptors, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/openFileDescriptors")); bufferedWriter.newLine();
		bufferedWriter.write("freeHeap, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/freeHeap")); bufferedWriter.newLine();
		bufferedWriter.write("maxHeap, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/maxHeap")); bufferedWriter.newLine();
		bufferedWriter.write("totalPhysicalMemory, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/totalPhysicalMemory")); bufferedWriter.newLine();
		bufferedWriter.write("freePhysicalMemory, slot:/Services/PlatformServices/SystemService, " + getBQLInteger(station, "station:|slot:/Services/PlatformServices/SystemService/freePhysicalMemory")); bufferedWriter.newLine();
		bufferedWriter.write("niagaraVersion, slot:/Services/PlatformServices/SystemService, " + getBQLString(station, "station:|slot:/Services/PlatformServices/SystemService/niagaraVersion")); bufferedWriter.newLine();
	    
	    bufferedWriter.close();
	  }
	  catch (Exception e)
	  {
	  	System.out.println("   *** Fatal Exception in createNameValueFile().");	
	  	System.out.println(e.toString());	
	  }
  }
  
  public BIcon getIcon() { return icon; }
  private static final BIcon icon = BIcon.make("local:|module://muninn/org/muninn/niagara/graphics/MuninnGroup.PNG");
  public static final Type TYPE = Sys.loadType(BAuditService.class);
  public Type[] getServiceTypes() { return new Type[] { TYPE }; }
  public Type getType() { return TYPE; }
  public final synchronized void serviceStarted() throws Exception { INSTANCE = this; }
  public final synchronized void serviceStopped() throws Exception { INSTANCE = null; }
  public static final BAuditService getInstance() { return INSTANCE; }
}

